package cn.xshi.common.util;

import cn.xshi.common.util.retry.ReTryUtil;
import com.github.rholder.retry.Attempt;
import com.github.rholder.retry.RetryListener;
import com.github.rholder.retry.Retryer;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.net.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
/**
 * @Desc TCP工具类
 * @Author 邓纯杰
 * @CreateTime 2012-12-12 12:12:12
 */
@Slf4j
public class TcpUtil {
    /**
     * 判断ip、端口是否可连接
     * @param host
     * @param port
     * @return
     */
    public static boolean isConnectable(String host, int port) {
        Socket socket = new Socket();
        try {
            socket.connect(new InetSocketAddress(host, port));
        } catch (IOException e) {
            log.error("ip不可用{0}",e.getMessage());
            return false;
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                log.error("关闭Socket出现异常{0}",e.getMessage());
            }
        }
        return true;
    }

    /**
     * 判断ip、端口是否可连接 并且追加超时时间
     * @param host
     * @param port
     * @param timeout
     * @return
     */
    public static boolean isConnectable(String host, int port,Integer timeout) {
        Socket socket = new Socket();
        try {
            SocketAddress socketAddress = new InetSocketAddress(host ,port);
            socket.connect(socketAddress,timeout); //设置超时参数
            //socket.setSoTimeout(10000);//设置的是读取/输入io流数据时的超时时间10 s
        } catch (IOException e) {
            log.error("ip不可用{0}",e.getMessage());
            return false;
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                log.error("关闭Socket出现异常{0}",e.getMessage());
            }
        }
        return true;
    }


    /**
     * 判断ip是否可以连接 timeOut是超时时间
     * @param host
     * @param timeOut
     * @return
     */
    public static boolean isHostReachable(String host, Integer timeOut) {
        try {
            return InetAddress.getByName(host).isReachable(timeOut);
        } catch (UnknownHostException e) {
            log.error("ip未知{0}",e.getMessage());
        } catch (IOException e) {
            log.error("Ping ip出现异常{0}",e.getMessage());
        }
        return false;
    }

    /**
     * 采用重试机制判断IP是否可用
     * @param ip ip地址
     * @param port 端口
     * @param timeOut 超时时间
     * @param times 间隔多少时间开始重试
     * @param reTryTimes 重试次数
     * @return
     */
    public static boolean ipIsable(String ip,Integer port,Integer timeOut,Integer times,Integer reTryTimes){
        ReTryUtil reTryUtil = new ReTryUtil();
        //判断目标地址是否通（2次机会）
        boolean isConnected = true;
        Callable<Boolean> callable = new Callable<Boolean>() {
            @Override
            public Boolean call(){
                try {
                    boolean f= TcpUtil.isConnectable(ip,new Integer(port),timeOut);//5秒无响应则重试
                    log.info("重试Nginx"+f);
                    return f;
                }catch (Exception e){
                    log.error("Retry Nginx出现异常");
                }
                return false;
            }
        };
        Retryer<Boolean> reTryer = null;
        try {
            reTryer =  reTryUtil.customRetryForUnException(callable,false,times,reTryTimes,new IpTryListener());//结果如果为false则10秒间隔重试机制
            isConnected = reTryer.call(callable).booleanValue();
            log.info("重试通讯结果："+isConnected);
        }catch (Exception e){
            log.error("重试通讯结果出现异常{0}",e.getMessage());
            isConnected = false;//重试始终得不到想要结果 则说明不可用
        }
        return isConnected;
    }

    /**
     * 重试监听器
     */
    static class IpTryListener  implements RetryListener {
        @Override
        public <Boolean> void onRetry(Attempt<Boolean> attempt) {
            // 第几次重试,(注意:第一次重试其实是第一次调用)
            System.out.print("[reTry]time=" + attempt.getAttemptNumber());

            // 距离第一次重试的延迟
            System.out.print(",delay=" + attempt.getDelaySinceFirstAttempt());

            // 重试结果: 是异常终止, 还是正常返回
            System.out.print(",hasException=" + attempt.hasException());
            System.out.print(",hasResult=" + attempt.hasResult());

            // 是什么原因导致异常
            if (attempt.hasException()) {
                System.out.print(",causeBy=" + attempt.getExceptionCause().toString());
            } else {
                // 正常返回时的结果
                System.out.print(",result=" + attempt.getResult());
            }
            try {
                Boolean result = attempt.get();
                System.out.print(",rude get=" + result);
            } catch (ExecutionException e) {
                System.err.println("this attempt produce exception." + e.getCause().toString());
            }
            System.out.println();
        }
    }
}
